# 04 — Symbology & Gotchas Every wrong-data bug found during the 2026-05-25/26 build came from a symbology surprise. Write them down so the next session doesn't re-discover them. ## DataBento CME smart symbols (parent symbols) | Product | Quarterly / monthly parent | Mon-Thu weeklies | Friday weeklies | |---|---|---|---| | ES (S&P 500 e-mini) | `ES.OPT` | `E1A.OPT` (Mon), `E1B.OPT` (Tue), `E1C.OPT` (Wed), `E1D.OPT` (Thu) | `EW1.OPT`, `EW2.OPT`, `EW3.OPT`, `EW4.OPT`, `EW.OPT` | | NQ (Nasdaq-100 e-mini) | `NQ.OPT` | `Q1A.OPT` (Mon), `Q1B.OPT` (Tue), `Q1C.OPT` (Wed), `Q1D.OPT` (Thu) | `QN1.OPT`, `QN2.OPT`, `QN3.OPT`, `QN4.OPT` | | GC (Gold) | **`OG.OPT`** (Options on Gold; the ticker is OG, not GC) | none | none — gold options are weekly+monthly only, no daily | | RTY (Russell 2000 e-mini) | **NOT IN DATABENTO CME FEED** — confirmed by enumerating all option-class assets in a 1h definition snapshot | — | — | **Critical:** Without pulling the weekly parents, the `.OPT` quarterly parent alone only contains the third-Friday-of-March/June/September/December options. So **0DTE and 7DTE buckets come up empty** unless you also query weekly parents. This bit me — first dashboard run showed "0 contracts" for ES 0DTE because I was only pulling `ES.OPT`. Solution: `databento_chain.fetch_chain_multi(dataset, [parents], trade_day, client)` merges results from a list of parents, deduping by `raw_symbol`. See `dashboard_visual.UNDERLYINGS` for the full per-product parent list. ## DataBento OPRA symbols | Product | Parents we pull | Note | |---|---|---| | **SPX** | `SPX.OPT` (AM-settled monthlies) + `SPXW.OPT` (weeklies) | Need BOTH for 0DTE/7DTE coverage. `SPX.OPT` alone is monthlies only. | | **NDX** | `NDX.OPT` + `NDXP.OPT` (weeklies, "PM-settled") | Same monthly-vs-weekly split | | **RUT** | `RUT.OPT` + `RUTW.OPT` (weeklies) | Same split | | Equity ETFs | Pulled via Schwab `/chains` instead (free) — not via DataBento | We don't pay OPRA for ETF chains because Schwab serves them at no cost | ## Schwab quote symbols (for spot lookup only) | What you want | Schwab `symbols=` parameter | Resolves to | |---|---|---| | ES front-month spot | `/ES` | `/ESM26` (June 2026) or whatever's front | | NQ front-month | `/NQ` | `/NQM26` | | RTY front-month | `/RTY` | `/RTYM26` | | GC front-month | `/GC` | `/GCM26` | | SPX index | `$SPX` | `$SPX` | | NDX index | `$NDX` | `$NDX` | | RUT index | `$RUT` | `$RUT` | | ETF | `SPY`, `QQQ`, `IWM`, `GLD` | exact symbol | `get_spot_via_schwab(symbol)` returns the first non-error key's `lastPrice` (falling back to `mark`, then `closePrice`). Symbol formats that **don't** work for chains queries on Schwab: - `$SPX.X` → 400 - `SPX` → 400 (without the `$`) - `SPXW`, `$SPXW` → 400 - `/ES`, `/NQ`, `/RTY`, `/GC` → all 400 (no options-on-futures support at all) ## Schwab data entitlement caveats - **`$SPX` chains return all contracts with `openInterest: 0`** under the non-pro market data agreement. Confirmed with `range`, `strategy`, `expMonth`, `optionType` parameters — none unlock index OI. Workaround = DataBento OPRA, not a Schwab config tweak. - Schwab greeks return `-999.0` outside RTH and sometimes during RTH on illiquid contracts. We compute BS greeks ourselves and ignore Schwab's published values. ## Contract multipliers (DataBento `unit_of_measure_qty`) | Product | M | What 1 contract represents | |---|---|---| | ES | 50 | 50 × index value (front-month future) | | NQ | 20 | 20 × index value | | RTY | 50 | 50 × index | | GC | 100 | 100 troy oz | | Equity / index options | 100 | 100 shares (or notional equiv. for indices) | OPRA definitions return `unit_of_measure_qty` as NaN for index options — fall back to 100 in `databento_chain.parse_chain` for any null/NaN. ## DataBento entitlement details (this account, 2026-05-25) Both `GLBX.MDP3` and `OPRA.PILLAR` are entitled. CME queries reported `$0.0000` for `get_cost` previews (i.e., included in subscription tier or below billing precision). OPRA queries are usage-based — see [[06 - Cost & Budget]]. OPRA historical data lags ~2 days. Live-tier subscription (separately priced) closes the gap. ## WSL/OneDrive 9p filesystem gotcha Once a path on `/mnt/c/` has been involved in a Windows-side rename or rapid-fire mv/rename operation, the WSL 9p cache can pin it as "missing" for the rest of the shell session. `wsl --shutdown` resets it. Result: **the project lives in `/home/dlitt/projects-personal/` (WSL ext4)**, with a periodic OneDrive backup at `/mnt/c/Users/dlitt/OneDrive/claude/projects-personal/schwab-options/`. Don't symlink across the boundary unless you're prepared for cache weirdness. ## See also - [[03 - Math & Sign Conventions]] for how multipliers flow through the GEX formula - [[05 - Dashboards & Run Guide]]